대용량 파일을 다운로드 받는 상황이다. 그런데 주는 쪽에서 구닥다리 euc-kr을 쓰고 있다. 나는 이 파일을 받으면서 필터링 가공을 하고 싶다.
예를 들어 10기가를 다운로드 받는데, 필터링 하면 50메가면 된다. 회사에서 주는 서버가 가상서버라서 메모리도 디스크도 쥐콩만한데, 백업까지 남기고 하면 뭐 불안불안한 상황이다.
그렇다면 스트림 처리를 해야 상책이다. 받으면서 처리하고 최후 결과만 저장한다.
중간 다 자르고 요약하면 아마도 이렇게 된다.
curl | iconv | my_filter.py
그런데 iconv는 스트림 처리를 하지 않나보다.. top 때리면 메모리 CPU 쭉 빨아먹는다. IO보다 CPU가 남아야 정상인데 CPU까지 빨아먹는다는 것은 청크단위 대량 복사에 의한 처리를 한다고 해석이 된다.
또 하나 스트림 처리는 난해한 구석이 있는데, 유니코드의 세계는 한 글자가 3바이트 또는 멀티바이트인 경우가 대부분이고, 다운로드되는 청크의 끝이 얼라인이 맞지 않으면 이를 디코딩 하기 어렵다. 또한 즐겨쓰는 sys.stdin 이터레이터를 쓰자니 개행문자를 인식못하여 파이프가 깨지고 뭔 쑈를 한다.
파이썬에서는 codesc를 쓰거나 유니코드 지원이 우아한 python3를 쓰는 것이 상책인데 스택오버플로에선 딱 맞는 답이 잘 안 보인다.
이렇게 한다.
sys.stdin, stdout은 fd가 아니므로 바로 익숙한 번호를 쓴다.
i = codecs.open(0, 'rb', encoding='euc-kr')
o = codecs.open(1, 'wb', encoding='utf-8')
with i:
while True:
c = i.read(4096)
if not c:
break
o.write(c)
decode.py의 모습이다. 청크를 받아서 이 코드에서 다 처리하려면 여전히 유니코드 멀티바이트 얼라인이 해소되지 않으므로 그냥 때려 쓰는 거다.
이제 다음 파이프에서 익숙한 방법으로 처리한다.
for line in sys.stdin:
헤헤헤
전체 모습은 이렇게 된다.
curl | ./decode.py | ./my_filter.py
메모리도 CPU도 디스크도 안정적이 된다.
sys.stdin.reconfigure 는 해결책이 되지 않는다. 아마도 파이프로 들어오는 바이트들이 코덱 멀티바이트 얼라인이 맞지 않아서 그런 것 같다. 파이프 깨짐 에러가 발생한 확률이 높다.